package com.loooooo.pm2024.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@ConditionalOnProperty(value = { "spring.redis.enabled" }, matchIfMissing = true)
@Configuration
@EnableCaching
@Order(1245871)
public class RedisConfiguration extends CachingConfigurerSupport {

    private final static Logger logger = LoggerFactory.getLogger(RedisConfiguration.class);


    @Value("${redis.password:}")
    String redisPassword;
    @Value("${redis.port:6379}")
    int redisPort;
    @Value("${redis.host:127.0.0.1}")
    String redisHost;
////
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setPassword(redisPassword);
        factory.setPort(redisPort);
        factory.setHostName(redisHost);
        return factory;
    }


    @Bean("keyGenerator")
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                //String key = RedisUtils.generateKey(target, method, params);
                //System.out.println("redis key="+key);
                return RedisUtils.generateKey(target, method, params);
            }
        };

    }

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Bean
    public RedisCacheManager cacheManager(  ) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
        //.disableCachingNullValues();
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
        logger.debug("自定义RedisCacheManager加载完成");
        return redisCacheManager;
    }

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate( ) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());
        logger.debug("自定义RedisTemplate加载完成");
        return redisTemplate;
    }
    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer<Object> valueSerializer() {
        //return new Jackson2JsonRedisSerializer<Object>(Object.class);
        return new JdkSerializationRedisSerializer();
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object o) {
                if (e instanceof RedisConnectionFailureException) {
                    logger.warn("redis has lose connection:", e);
                    return;
                } else if (e instanceof SerializationException) {
                    cache.evict(o);
                    return;
                }
                throw e;
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object o, Object o1) {
                if (e instanceof RedisConnectionFailureException) {
                    logger.warn("redis has lose connection:", e);
                    return;
                }
                throw e;
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object o) {
                throw e;

            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                throw e;

            }
        };
    }

    static MessageDigest md;

    static {
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    private static String md5If(String text) {
        if (text == null) return text;
        //MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] md5 = md.digest(text.getBytes(StandardCharsets.UTF_8));
        StringBuffer buf = new StringBuffer("");
        for (int offset = 0; offset < md5.length; offset++) {
            int i = md5[offset];
            if (i < 0)
                i += 256;
            if (i < 16)
                buf.append("0");
            buf.append(Integer.toHexString(i));
        }
        return buf.toString();

    }


}
